package cn.wlinker.video.monitor.brand.hikversion.create.net;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.log.StaticLog;
import cn.wlinker.video.monitor.utils.CommonKitUtils;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.ByteByReference;
import com.sun.jna.ptr.IntByReference;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;

/**
 * 播放库抓图
 *
 * @author gxsjx
 * @version 1.0
 * @date 2022/11/24
 */
@Slf4j
public class HCNetRealPlay {

    static FRealDataCallBack fRealDataCallBack;//预览回调函数实现
    static PlayCtrl playControl;
    static HCNetSDK hCNetSDK;

    static int lPlay = -1;  //预览句柄
    static int lCapture = -1;  //抓图句柄

    static IntByReference m_lPort = new IntByReference(-1);

    /**
     * 超时时间,单位秒
     */
    private static final int timeOutSecond = 3;

    //使用单元素阻塞队列 指定时间内若未收到回复则返回失败
    static SynchronousQueue<Object> synchronousQueue = new SynchronousQueue<>();

    static {
        playControl = (PlayCtrl) Native.loadLibrary(CommonKitUtils.getPlayLibPath(), PlayCtrl.class);
        hCNetSDK = HCNetTools.hCNetSDK;
    }

    @SneakyThrows
    public static Boolean realPlayAndSavePic(int userID, int iChannelNo, String imgPath) {
        Assert.isTrue(userID != -1, "请先注册");
        //初始化
        lCapture = -1;

        HCNetSDK.NET_DVR_PREVIEWINFO strClientInfo = new HCNetSDK.NET_DVR_PREVIEWINFO();
        strClientInfo.read();
        strClientInfo.hPlayWnd = 0;  //窗口句柄，从回调取流不显示一般设置为空
        strClientInfo.lChannel = iChannelNo;  //通道号
        strClientInfo.dwStreamType = 0; //0-主码流，1-子码流，2-三码流，3-虚拟码流，以此类推
        strClientInfo.dwLinkMode = 0; //连接方式：0- TCP方式，1- UDP方式，2- 多播方式，3- RTP方式，4- RTP/RTSP，5- RTP/HTTP，6- HRUDP（可靠传输） ，7- RTSP/HTTPS，8- NPQ
        strClientInfo.bBlocked = 1;
        strClientInfo.write();

        //回调函数定义必须是全局的
        if (fRealDataCallBack == null) {
            fRealDataCallBack = new FRealDataCallBack();
        }

        //开启预览
        lPlay = hCNetSDK.NET_DVR_RealPlay_V40(userID, strClientInfo, fRealDataCallBack, null);
        Assert.isTrue(lPlay != -1, "取流失败" + hCNetSDK.NET_DVR_GetLastError());
        //StaticLog.info("取流成功");
        //使用单元素阻塞队列 指定时间内若未收到回复则返回失败
        Object poll = synchronousQueue.poll(timeOutSecond, TimeUnit.SECONDS);
        Assert.notNull(poll, "码流获取失败");
        boolean flag = false;
        //尝试三次,避免失败
        for (int i = 0; i < 3; i++) {
            Thread.sleep(200);
            flag = savePicByPlayCtrl(imgPath);
            if (flag) {
                break;
            }
        }
        if (hCNetSDK.NET_DVR_StopRealPlay(lPlay) && lPlay >= 0) {
            StaticLog.info("停止预览成功");
        }
        return flag;
    }

    /**
     * 播放库抓图
     *
     * @param imgPath
     * @return
     */
    @SneakyThrows
    private static boolean savePicByPlayCtrl(String imgPath) {
        IntByReference pWidth = new IntByReference(0);
        IntByReference pHeight = new IntByReference(0);
        boolean bFlag = playControl.PlayM4_GetPictureSize(m_lPort.getValue(), pWidth, pHeight);
        if (!bFlag) {
            StaticLog.info("获取失败：" + playControl.PlayM4_GetLastError(m_lPort.getValue()));
            return false;
        }
        //StaticLog.info(pWidth.getValue());
        //StaticLog.info(pHeight.getValue());
        IntByReference RealPicSize = new IntByReference(0);
        int picSize = pWidth.getValue() * pHeight.getValue() * 5;
        HCNetSDK.BYTE_ARRAY picByte = new HCNetSDK.BYTE_ARRAY(picSize);
        if (picSize == 0) {
            return false;
        }
        picByte.write();
        Pointer pByte = picByte.getPointer();
        boolean b_GetPic = playControl.PlayM4_GetJPEG(m_lPort.getValue(), pByte, picSize, RealPicSize);
        if (!b_GetPic) {
            //StaticLog.info("抓图失败：" + playControl.PlayM4_GetLastError(m_lPort.getValue()));
            return false;
        }
        picByte.read();
        try (FileOutputStream fileOutputStream = new FileOutputStream(imgPath)) {
            //将字节写入文件
            long offset = 0;
            ByteBuffer buffers = pByte.getByteBuffer(offset, RealPicSize.getValue());
            byte[] bytes = new byte[RealPicSize.getValue()];
            buffers.rewind();
            buffers.get(bytes);
            fileOutputStream.write(bytes);
            fileOutputStream.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            log.error("播放库抓图异常", e);
        }
        //StaticLog.info("抓图成功!");
        return true;
    }

    /**
     * 预览回调
     */
    static class FRealDataCallBack implements HCNetSDK.FRealDataCallBack_V30 {
        //预览回调
        public void invoke(int lRealHandle, int dwDataType, ByteByReference pBuffer, int dwBufSize, Pointer pUser) throws InterruptedException {
            //StaticLog.info("码流数据回调...dwBufSize=" + dwBufSize);
            //播放库解码
            switch (dwDataType) {
                case HCNetSDK.NET_DVR_SYSHEAD: //系统头
                    if (!playControl.PlayM4_GetPort(m_lPort)) //获取播放库未使用的通道号
                    {
                        break;
                    }
                    if (dwBufSize > 0) {
                        if (!playControl.PlayM4_SetStreamOpenMode(m_lPort.getValue(), PlayCtrl.STREAME_REALTIME))  //设置实时流播放模式
                        {
                            break;
                        }
                        if (!playControl.PlayM4_OpenStream(m_lPort.getValue(), pBuffer, dwBufSize, 1024 * 1024)) //打开流接口
                        {
                            break;
                        }
                        if (!playControl.PlayM4_Play(m_lPort.getValue(), null)) //播放开始
                        {
                            break;
                        }

                    }
                case HCNetSDK.NET_DVR_STREAMDATA:   //码流数据
                    if ((dwBufSize > 0) && (m_lPort.getValue() != -1)) {
                        if (!playControl.PlayM4_InputData(m_lPort.getValue(), pBuffer, dwBufSize))  //输入流数据
                        {
                            break;
                        }
                    }
                    //修改抓拍句柄并发送通知
                    if (lCapture > 0) {
                        return;
                    }
                    lCapture = 1;
                    ThreadUtil.execute(() -> {
                        if (synchronousQueue == null) {
                            return;
                        }
                        try {
                            synchronousQueue.offer(new Object(), timeOutSecond, TimeUnit.SECONDS);
                        } catch (InterruptedException e) {
                            log.error("预览回调异常", e);
                        }
                    });
            }
        }
    }
}
